#include "BinaryStandardFormatLogger.hpp"
#include "../stack_trace/stack_trace.h"
#include "alloc_stats.h"

/*---manipulation de fichier---*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <cstdlib>


/*---manipulation de chaines---*/
#include <string.h>

/*------------IPC--------------*/
#include "ipc.h"
        

/*-----------------------------*/
#define X86_64 1

#define ROOT "memoryTrace"
#define PID  "pid"
#define PPID "ppid"
#define EXE  "exe"




/*-----------------------------------------*/
/**
 *\brief 
 */
static unsigned int shared_mem_initialised=0;
static unsigned int ipc_finalized=0;

/**
 * \brief buffer ou mettre les appels d'allocation
 */
static alloc_event_t* buffer_allocs;

/**
 * \brief buffer ou lettre l'origine des appels d'allocation
 */
#ifndef WITH_PTHREADS
static symbole_t* buffer_stack_traces;
#endif
/**
 * \brief contient la position du courant appels d'allocation
 */
static int id_current_event=0;

/**
 * \brief contient le nombre d'events module MAX_ELEMENTS
 */
static unsigned long* events_count_mod;
/**
 * Retourne l'adresse ou placer le i alloc event
 */
#define getBuffer(i) (buffer_allocs+i);

#ifndef WITH_PTHREADS
#define getBufferStackTraces(i) (buffer_stack_traces+i)
#endif

/**
 *\brief dictionnaire utilisé pour code les noms de fonction
 */
static const char dictionnary[4]={'m','c','r','f'};

/**
 * 
 * @param buf
 * @param l
 */
#define getValue(key) dictionnary[key]
#define getByteAt(bytes,idx) ((char*) &bytes)[idx]

#ifdef X86_64
#define ADDR_T long unsigned int
#else   
#define ADDR_T long unsigned int
#endif


#define RED    "[31;1m"
#define BLUE   "[34;1m"
#define CYAN   "[36;1m"
#define GREEN  "[32;1m"
#define YELLOW "[33;1m"
#define CANCEL "[00;1m"

#define PERIODE_RAFFRAICHISSEMENT 10

static int couleur=0;
static unsigned long int compteur_messages=1;


inline void showMessageRecodeFailed(){
    printf("La capacité mémoire de l'instrumentateur est dépassée, fermeture des ipc, demande à l'instrumenté de fermer les siennes.\n");   
}


inline void showMessageRecordSuccess(){
    
  
  if(compteur_messages%PERIODE_RAFFRAICHISSEMENT ==0){
    if(couleur==0){
      printf("\033[34;1m(R%ld)Instrumenter process recorded %ld more allocation events.\n\033[m",compteur_messages+1,
	     (unsigned long int)MAX_ELEMENTS*PERIODE_RAFFRAICHISSEMENT);
    }
    else{
      printf("\033%s(R%ld)Instrumenter process recorded %ld more allocation events.\n\033[m",RED,compteur_messages+1,
	     (unsigned long int)MAX_ELEMENTS*PERIODE_RAFFRAICHISSEMENT);
    }
    couleur=(couleur+1)%2;
    //@printf "\033[34;1m\nCompilation de la version minimale de la lib interposer.\n\033[m"
  }
  compteur_messages++; 
}





static long ret=0;

/**
 * \brief
 * @param out
 */
inline void sendBuffer(BinaryStandardFormatLogger* me){
     
  *events_count_mod=1+(id_current_event%MAX_ELEMENTS);
  if(id_current_event%MAX_ELEMENTS==0){
      
        //#ifdef DEBUG
     //printf("Envoi du signal shmem pleinne\n");
        //#endif
      
      
      /*on ecrit le nombre d'elements valide en shared memory*/
      (*events_count_mod)=MAX_ELEMENTS;
      
      /*on signal au consommateur que la shared memory est pleine*/
      MESSAGE_QUEUE<QID_1>::sendMessageToConsumer(MSG_TAG_PLEIN,MAX_ELEMENTS);
      
      /**
       * on attend que le consommateur nous signale qu'il a terminé
       * de copier dans sa mémoire locale la shmem
       */
      #ifdef DEBUG
      printf("Je suis le producteur j'attends le consommateur\n");
      #endif
      ret=MESSAGE_QUEUE<QID_1>::receiveMessageFromConsumer(MSG_TAG_RECU); 
      
      /*Si echec de reception alors on quitte l'instrumentation*/
      if(ret==-1){
          showMessageRecodeFailed();
          me->finitLogger();
          exit(EXIT_FAILURE);
      }
      //#ifdef DEBUG
      showMessageRecordSuccess();   
      //#endif
  }
   
  //il faut ajouter un autre buffer en shmem, afin de pas bloquer ce processus
  
  //Se mettre en attente de la reponse de fin du processus compresseur
}

/**
 * \brief
 */
void BinaryStandardFormatLogger::openFile(){   
}

/**
 *\brief 
 */
BinaryStandardFormatLogger::BinaryStandardFormatLogger(){
    printf("Initialisation du loggeur binaire, element size=%d\n",(int)EVENT_SIZE);
    /*Ici mettre en place les segments de mémoire partagé*/
}


/**
 * \brief
 */
void BinaryStandardFormatLogger::initLogger(){
    if(shared_mem_initialised==0){
        //Initialisation de la shared memory
        SharedMemory<MAX_ELEMENTS*EVENT_SIZE,IPC_KEY_1>::makeKey();
        SharedMemory<MAX_ELEMENTS*EVENT_SIZE,IPC_KEY_1>::connectToSegment();
        buffer_allocs=(alloc_event_t*)SharedMemory<MAX_ELEMENTS*EVENT_SIZE,IPC_KEY_1>::getPointerToShmem();


        /*initialisation d'un compteur d'evenement en shared memory*/
        SharedMemory<sizeof(unsigned long int),IPC_KEY_2>::makeKey();
        SharedMemory<sizeof(unsigned long int),IPC_KEY_2>::connectToSegment();
        events_count_mod=(unsigned long *)SharedMemory<sizeof (unsigned long int),IPC_KEY_2>::getPointerToShmem();
        
#ifndef WITH_PTHREADS
        /*initialisation d'une shared memory pour les backtraces*/
        SharedMemory<MAX_ELEMENTS*STACK_TRACE_SIZE,IPC_KEY_3>::makeKey();
        SharedMemory<MAX_ELEMENTS*STACK_TRACE_SIZE,IPC_KEY_3>::connectToSegment();
        buffer_stack_traces=(symbole_t*)SharedMemory<MAX_ELEMENTS*STACK_TRACE_SIZE,IPC_KEY_3>::getPointerToShmem();
#endif
        
        //Initialisation de la MESSAGE_QUEUE
        MESSAGE_QUEUE<QID_1>::initQueues();
       
        
        
        //Affiche un message disant que c'est bon
        printf("Initializing queues done\n");
        shared_mem_initialised=1;
        /*----------------------------------------------------*/
    }   
    openFile();
}

void BinaryStandardFormatLogger::finitLogger(){
    
    
    if(ipc_finalized==0){
        printf("\033[34;1mUn thread du processus instrumenté finalise le loggeur reste %d evenements.\n\033[m",id_current_event);


        MESSAGE_QUEUE<QID_1>::sendMessageToConsumer(MSG_TAG_QUIT,id_current_event);

        //Detachement des segments de mémoire partagées
        SharedMemory<MAX_ELEMENTS * EVENT_SIZE, IPC_KEY_1> ::detachSegment();
        SharedMemory<sizeof(unsigned long int),IPC_KEY_2>::detachSegment();

    #ifndef WITH_PTHREADS
        SharedMemory<MAX_ELEMENTS*STACK_TRACE_SIZE,IPC_KEY_3>::detachSegment();
    #endif
        ipc_finalized=1;
    }
}

BinaryStandardFormatLogger::~BinaryStandardFormatLogger(){
};


void BinaryStandardFormatLogger::startMalloc(void* addr,size_t size,unsigned int index,int total_memory){
  
  alloc_event_t* event;

#ifndef WITH_PTHREADS
  symbole_t *symbole;
#endif
  
  /*utilisation des autres arguments*/
  index++;
  total_memory++;
  
  /*buffer qui va contenir le fragment*/
  event = getBuffer(id_current_event);
  
  
  /*On remplit la structure du fragment de trace*/
  event->addr_realloc=(void*)0;
  event->size=size;
  event->nmemb=0;
  event->type=getValue(id_malloc);
  event->ret =addr;

  /*On recupère l'orgine de ce malloc*/
#ifndef WITH_PTHREADS
  symbole=getBufferStackTraces(id_current_event);
  getCaller(symbole);
  //printf("Origine du malloc[%d]::<%s>\n",id_current_event,symbole->symb);  
#endif
  id_current_event=(id_current_event+1)% MAX_ELEMENTS;
  sendBuffer(this);
  }

void BinaryStandardFormatLogger::startCalloc(void* addr,size_t nmemb,size_t size,
					int id,unsigned int memory_allocation)
{  
  //printf("START CALLOC %d\n",id);  
  alloc_event_t* event;
  
#ifndef WITH_PTHREADS
  symbole_t* symbole;
#endif
  
  /*utilisation des autres arguments*/
  id++;
  memory_allocation++;
  /*---------------------------------*/
  
  event = getBuffer(id_current_event);
  
  
  /*On remplit la structure du fragment de trace*/
  event->addr_realloc=(void*)0;
  event->size=size;
  event->nmemb=nmemb;
  event->type=getValue(id_calloc);
  event->ret =addr;
  
  
#ifndef WITH_PTHREADS
  symbole=getBufferStackTraces(id_current_event);
  getCaller(symbole);
  //printf("Origine du calloc::<%s>\n",symbole->symb);
#endif
  /*on incremente le buffer de 1 circulairement*/
  id_current_event=(id_current_event+1)% MAX_ELEMENTS;  
  
  /*On ecrit dans le fichier les MAX_ELEMENTS du buffer*/  
  sendBuffer(this); 
}

void BinaryStandardFormatLogger::startRealloc(void*  v,void*  ret,size_t n,int id,size_t tm){
  //printf("START REALLOC %d\n",id);
  
#ifndef WITH_PTHREADS
  symbole_t *symbole;
#endif  
  
  alloc_event_t* event;
  event = getBuffer(id_current_event);
    

  /*utilisation des autres arguments*/
  id++;
  tm++;
  /*---------------------------------*/
  
  /*On remplit la structure du fragment de trace*/
  event->addr_realloc=v;
  event->size=n;
  event->nmemb=0;
  event->type=getValue(id_realloc);
  event->ret =ret;
  
  
#ifndef WITH_PTHREADS
  symbole=getBufferStackTraces(id_current_event);
  getCaller(symbole);
  //printf("Origine du realloc: <%s>\n",symbole->symb);
#endif
  /*on incremente le buffer*/
  id_current_event=(id_current_event+1)% MAX_ELEMENTS;
  /*On ecrit dans le fichier les MAX_ELEMENTS du buffer*/  
  sendBuffer(this);   
}

void BinaryStandardFormatLogger::startFree(void* addr,unsigned nbfree){
  //printf("START FREE %d\n",nbfree);
  alloc_event_t* event;
  
#ifndef WITH_PTHREADS
  symbole_t* symbole;
#endif
  
  /*utilisation des autres arguments*/
  nbfree++;
  /*---------------------------------*/
  
  event = getBuffer(id_current_event);  
  /*On remplit la structure du fragment de trace*/
  event->addr_realloc=(void*)0;
  event->size=0;
  event->nmemb=0;
  event->type=getValue(id_free);
  event->ret =addr;
  
#ifndef WITH_PTHREADS
  symbole=getBufferStackTraces(id_current_event);
  getCaller(symbole);
  //printf("Origine du free: <%s>\n",symbole->symb);
#endif

  id_current_event=(id_current_event+1)% MAX_ELEMENTS;
  /*On ecrit dans le fichier les MAX_ELEMENTS du buffer*/  
  sendBuffer(this);
}

void BinaryStandardFormatLogger::endElement(const char *type){
    
}


void BinaryStandardFormatLogger::finalizeLog(alloc_stats & stats){
}
